#ifndef __CELLOS_LV2__
#error You need to patch me first !
#else

#include <SDL.h>
#include <stdio.h>
#include <dirent.h>
#include <SDL_image.h>
#include <SDL_cellhacks.h>

#include <sysutil/sysutil_msgdialog.h>

#include "i_loader.h"
#include "dirlist.h"
#include "message.h"
#include "options.h"

/* Wad data */
struct LoaderGame*			Games[MAX_GAMES];
uint32_t					FoundGames;

struct PWAD					PWADS[MAX_PWADS];
uint32_t					FoundPWADS;

struct PWAD					DEH[MAX_PWADS];
uint32_t					FoundDEH;

static const char*			StandardIWADS[] = 
{
  "doom.wad",
  "doom1.wad",
  "doom2f.wad",
  "doom2.wad",
  "plutonia.wad",
  "tnt.wad",
  "doomu.wad",
  "freedoom.wad"
};

/* Like strstr but case insensitive */
char*						strcasestr					(char* aHaystack, const char* aNeedle)
{
	int hln = strlen(aHaystack);
	int nln = strlen(aNeedle);

	for(int i = 0; i != hln; i ++)
	{
		if(strncasecmp(&aHaystack[i], aNeedle, nln) == 0)
		{
			return &aHaystack[i];
		}
	}

	return 0;
}

/* GAME LIST */
/* 0: Not wad, 1: IWAD, 2: PWAD */
static uint32_t				WADS_GetType					(const char* aPath)
{
	char id[5];
	FILE* f = fopen(aPath, "rb");

	if(f)
	{
		fread(id, 4, 1, f);
		id[4] = 0;

		fclose(f);

		if(strcmp(id, "IWAD") == 0)
		{
			return 1;
		}

		if(strcmp(id, "PWAD") == 0)
		{
			return 2;
		}
	}

	return 0;
}

static void					WADS_DeleteIWADS			()
{
	int i;

	/* Kill iwads */
	for(i = 0; i != FoundGames; i ++)
	{
		if(Games[i])
		{
			if(Games[i]->Image)
			{
				SDL_FreeSurface(Games[i]->Image);
			}

			free(Games[i]);
		}
	}

	FoundGames = 0;
}

static void					WADS_DeletePWADS			()
{
	memset(PWADS, 0, sizeof(PWADS));
	FoundPWADS = 0;
}

static void					WADS_DeleteDEH				()
{
	memset(DEH, 0, sizeof(PWADS));
	FoundDEH = 0;
}

static SDL_Surface*			WADS_GetImage				(struct DirectoryList* aImages, const char* aGameTitle)
{
	char image[MAX_PATH];
	struct DirectoryList* Image;
	static SDL_Color bgcolor={0,0,0}, color={0xff,0xff,0xff,0xff};
	static const char* ImageFormats[] = {"bmp", "png", "jpg", "jpeg", "tif", "tiff"};

	SDL_Surface* out;
	SDL_Surface* text;

	IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF);

	for(int i = 0; i != 6; i ++)
	{
		snprintf(image, MAX_PATH, "%s.%s", aGameTitle, ImageFormats[i]);

		if((Image = DIRLIST_FindFile(aImages, image)) && (out = IMG_Load(Image->FullPath)))
		{
			return out;
		}
	}

	out = SDL_CreateRGBSurface(0, 320, 200, 32, 0xFF0000, 0xFF00, 0xFF, 0xFF000000);
	text = STRING_Render(aGameTitle, color, bgcolor);
	SDL_BlitSurface(text, 0, out, 0);
	SDL_FreeSurface(text);

	return out;
}

static int					WADS_ScanIWADS				()
{
	struct DirectoryList* list = DIRLIST_Create(WAD_PATH);
	struct DirectoryList* images = DIRLIST_Create(IMG_PATH);
	struct DirectoryList* Game;

	WADS_DeleteIWADS();

	if(list)
	{
		for(int i = 0; i != WAD_COUNT; i ++)
		{
			if((Game = DIRLIST_FindFile(list, StandardIWADS[i])))
			{
				struct LoaderGame* g;

				/* Allocate the game */
				g = malloc(sizeof(struct LoaderGame));
				memset(g, 0, sizeof(struct LoaderGame));

				strncpy(g->WAD, Game->FullPath, MAX_PATH);
				strncpy(g->BaseName, Game->Item.d_name, 256);
				if(strrchr(g->BaseName, '.'))
				{
					*strrchr(g->BaseName, '.') = '\0';
				}
				for(int j = 0; j != strlen(g->BaseName); j ++)
				{
					g->BaseName[j] = tolower((unsigned char)g->BaseName[j]);
				}

				/* Load the image */
				g->Image = WADS_GetImage(images, g->BaseName);


				/* Add to the list */
				Games[FoundGames ++] = g;

				if(FoundGames == MAX_GAMES)
				{
					break;
				}
			}
		}
	}

	DIRLIST_Delete(list);
	DIRLIST_Delete(images);

	return FoundGames;
}

static int					WADS_ScanPWADS				()
{
	struct DirectoryList* list = DIRLIST_Create(WAD_PATH);
	struct DirectoryList* current = list;

	WADS_DeletePWADS();

	while(current && (FoundPWADS < MAX_PWADS))
	{
		if(WADS_GetType(current->FullPath) == 2)
		{
			strncpy(PWADS[FoundPWADS].WAD, current->FullPath, MAX_PATH);
			strncpy(PWADS[FoundPWADS].BaseName, current->Item.d_name, 256);
			if(strrchr(PWADS[FoundPWADS].BaseName, '.'))
			{
				*strrchr(PWADS[FoundPWADS].BaseName, '.') = '\0';
			}
			for(int j = 0; j != strlen(PWADS[FoundPWADS].BaseName); j ++)
			{
				PWADS[FoundPWADS].BaseName[j] = tolower((unsigned char)PWADS[FoundPWADS].BaseName[j]);
			}

			FoundPWADS ++;
		}

		current = current->Next;
	}

	DIRLIST_Delete(list);

	return FoundPWADS;
}

static int					WADS_ScanDEH				()
{
	struct DirectoryList* list = DIRLIST_Create(WAD_PATH);
	struct DirectoryList* current = list;

	WADS_DeleteDEH();

	while(current && (FoundDEH < MAX_PWADS))
	{
		if(strcasestr(current->Item.d_name, ".deh") || strcasestr(current->Item.d_name, ".bex"))
		{
			strncpy(DEH[FoundDEH].WAD, current->FullPath, MAX_PATH);
			strncpy(DEH[FoundDEH].BaseName, current->Item.d_name, 256);
			if(strrchr(DEH[FoundDEH].BaseName, '.'))
			{
				*strrchr(DEH[FoundDEH].BaseName, '.') = '\0';
			}
			for(int j = 0; j != strlen(DEH[FoundDEH].BaseName); j ++)
			{
				DEH[FoundDEH].BaseName[j] = tolower((unsigned char)DEH[FoundDEH].BaseName[j]);
			}

			FoundDEH ++;
		}

		current = current->Next;
	}

	DIRLIST_Delete(list);

	return FoundDEH;
}


static void DrawGames(SDL_Surface* aScreen, uint32_t aSelect)
{
	/* Regions */
	static SDL_Rect imagetocs[MAX_GAMES] = {{0, 0, 328, 208}, {328, 0, 328, 208}, {0, 208, 328, 208}, {328, 208, 328, 208}};
	static SDL_Rect imagelocs[MAX_GAMES] = {{4, 4, 320, 200}, {332, 4, 320, 200}, {4, 212, 320, 200}, {332, 212, 320, 200}};

	if(aSelect < MAX_GAMES)
	{
		/* Draw background */
		SDL_FillRect(aScreen, 0, 0);
		SDL_FillRect(aScreen, &imagetocs[aSelect], 0xFF0000FF);

		/* Draw games */
		for(int i = 0; i != FoundGames; i ++)
		{
			SDL_BlitSurface(Games[i]->Image, 0, aScreen, &imagelocs[i]);
		}

		SDL_Flip(aScreen);
	}
}


/**** WAD FUNCTIONS ****/
/* Return 1 if the first four bytes of file at aPath are 'IWAD' */

static void				UpdateProgress				(off_t aNow, off_t aTotal)
{
	cellMsgDialogProgressBarInc(1, 1);

	SDL_PumpEvents();
	SDL_Flip(SDL_GetVideoSurface());
}

/* Copy file aPath to /dev_hdd0/game/HBDM90000/USRDIR/wad/{aTarget} */
static void				InstallWad					(const char* aPath, const char* aTarget)
{
	char buffer[MAX_PATH];

	cellMsgDialogProgressBarSetMsg(1, aTarget);
	snprintf(buffer, MAX_PATH, "/dev_hdd0/game/HBDM90000/USRDIR/wad/%s", aTarget);

	DIRLIST_CopyFile(aPath, buffer, UpdateProgress);

	cellMsgDialogProgressBarReset(1);
	cellMsgDialogProgressBarInc(0, 10);
}

/* Scan root for folders name /dev_usb* and serach them for files ending in .wad */
static void				InstallWads					()
{
	if(MSG_YesNo("Would you like to search the root of attached USB devices for IWAD files?\n\nAny IWAD files located will be copied onto the internal hard drive."))
	{
		struct DirectoryList* root = DIRLIST_Create("/");
		struct DirectoryList* current = root;

		int button = 0;
		while(CELL_SYSUTIL_ERROR_BUSY == cellMsgDialogOpen2(CELL_MSGDIALOG_TYPE_SE_TYPE_NORMAL | 
					CELL_MSGDIALOG_TYPE_DISABLE_CANCEL_ON | CELL_MSGDIALOG_TYPE_PROGRESSBAR_DOUBLE, 
					"Installing", msgCallback, &button, 0));

		while(current)
		{
			if(strstr(current->Item.d_name, "dev_usb"))
			{
				struct DirectoryList* thisusb = DIRLIST_Create(current->FullPath);
				struct DirectoryList* currentusb = thisusb;

				while(currentusb)
				{
					if(strcasestr(currentusb->Item.d_name, ".wad") && WADS_GetType(currentusb->FullPath))
					{
						InstallWad(currentusb->FullPath, currentusb->Item.d_name);
					}

					/* DEH and BEX, no tests... */
					if(strcasestr(currentusb->Item.d_name, ".deh"))
					{
						InstallWad(currentusb->FullPath, currentusb->Item.d_name);
					}

					if(strcasestr(currentusb->Item.d_name, ".bex"))
					{
						InstallWad(currentusb->FullPath, currentusb->Item.d_name);
					}


					currentusb = currentusb->Next;
				}

				DIRLIST_Delete(thisusb);
			}

			current = current->Next;
		}

		DIRLIST_Delete(root);

		cellMsgDialogClose(0);
	}
}

static const CELL_FixedMap keymapping = {HID_NOEVENT, HID_NOEVENT, HID_NOEVENT, HID_RETURN, HID_UP, HID_RIGHT, HID_DOWN, HID_LEFT, HID_NOEVENT, HID_NOEVENT, HID_NOEVENT, HID_NOEVENT, HID_TAB, HID_ESCAPE, HID_RETURN, HID_F12};
int				I_WantToRunTheLoader				(int* argc, char*** argv)
{
	SDL_Surface* screen;
	SDL_Event e;
	uint32_t select = 0;
	int32_t result = 0;

	/* Init and set video mode here so dialog prompts can show */
	if(!SDL_WasInit(SDL_INIT_VIDEO))
	{
		SDL_Init(SDL_INIT_VIDEO);
	}

	screen = SDL_SetVideoMode(656, 416, 32, 0);

	/* Setup input */
	SDL_ShowCursor(SDL_DISABLE);
	CELL_PAD_SetMode(0, KEYBOARD);
	CELL_PAD_SetFixedMap(&keymapping);

	/* Look for games, If none are found and the user asks, search usb devices for them */
	if(!WADS_ScanIWADS())
	{
		InstallWads();
		WADS_ScanIWADS();
	}

	/* We STILL don't have any games, abort. */
	if(!FoundGames)
	{
		MSG_Normal("No IWADs were found. Place IWADs on the root of a USB drive and run the program again.");
		SDL_Quit();
		return -1;
	}

	WADS_ScanPWADS();
	WADS_ScanDEH();

	/* Draw the games */
	DrawGames(screen, select);

	/* Loader main loop */
	while(SDL_WaitEvent(&e))
	{
		uint32_t oldselect = select;

		if(e.type == SDL_KEYDOWN)
		{
			if(e.key.keysym.sym == SDLK_LEFT || e.key.keysym.sym == SDLK_RIGHT)
			{
				select ^= 1;
			}
			else if(e.key.keysym.sym == SDLK_DOWN || e.key.keysym.sym == SDLK_UP)
			{
				select ^= 2;
			}
			else if(e.key.keysym.sym == SDLK_RETURN)
			{
				result = OPTIONS_Dont(argc, argv, select);
				break;
			}
			else if(e.key.keysym.sym == SDLK_TAB)
			{
				result = OPTIONS_Do(argc, argv, select);
				break;
			}
			else if(e.key.keysym.sym == SDLK_F12)
			{
				InstallWads();
				WADS_ScanIWADS();
				WADS_ScanPWADS();
				WADS_ScanDEH();
				DrawGames(screen, select);
			}
			else if(e.key.keysym.sym == SDLK_ESCAPE)
			{
				result = -1;
				break;
			}
		}
		/* Close window */
		else if(e.type == SDL_QUIT)
		{
			result = -1;
			break;
		}

		/* Redraw the screen */
		if(select != oldselect)
		{
			DrawGames(screen, select);
		}
	}

	/* Quit sdl if we are aborting */
	if(result < 0)
	{
		SDL_Quit();
	}

	return result;
}

#endif

